/*
 * 
 * Copyright (C) 2010 Wilfried Elmenreich, Anita Sobe 
 * This file is part of VideoNetwork.
 * 
 * VideoNetwork is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>. 
 */
package main;

import java.util.ArrayList;
import java.util.Vector;

import consumer.ViewingCount;

import proxy.ServerNode;

import simulation.Optimizer;
import simulation.OutputGenerator;
import simulation.Simulator;
import simulation.SimulatorConfig;
import simulation.StatKeeper;

//final configuration: 9GB per node, 5-10MB per video unit

public class Main {

	static ArrayList<StatKeeper> notableStats = new ArrayList<StatKeeper>();
	static StatKeeper timeStat = new StatKeeper(true, "1:time"); // give the odd

	// name in
	// order to
	// have this
	// column
	// sorted
	// first

	//TODO make this method readable!!!
	static void singleRun(int seed) {

		int nServers = 2;
		int simTime = 50;
		
		int nLosses = 1;
		int cLosses = 2;

		timeStat.clear();

		//Add statistics:
		//1) number of fullfilled reqeusts
		StatKeeper seqRequestsDone = new StatKeeper(true, "numRequestsDone."
				+ seed);
		notableStats.add(seqRequestsDone);

		//2) number of move actions
		StatKeeper migrationMoves = new StatKeeper(true, "migrationMoves."
				+ seed);
		notableStats.add(migrationMoves);
		//3) number of copy actions
		StatKeeper migrationCopies = new StatKeeper(true, "migrationCopies."
				+ seed);
		notableStats.add(migrationCopies);
		//4) delay from request to fulfillment
		StatKeeper avDelay = new StatKeeper(true, "avDelay." + seed);
		notableStats.add(avDelay);
		//5) throughput
		StatKeeper thruput = new StatKeeper(true, "thruPut." + seed);
		notableStats.add(thruput);
		//6) requests fulfilled in relation to requests sent
		StatKeeper viewingFulfilment = new StatKeeper(true,
				"viewingFulfilment." + seed);
		notableStats.add(viewingFulfilment);
		//7)average disk usage
		StatKeeper averageDiskUsage = new StatKeeper(true, "averageDiskUsage."
				+ seed);
		notableStats.add(averageDiskUsage);
		//
//		StatKeeper worstcaseDiskUsage = new StatKeeper(true,
//				"worstcaseDiskUsage." + seed);
//		StatKeeper hitrate = new StatKeeper(true, "hitrate." + seed);
//		notableStats.add(hitrate);
	
		StatKeeper delayCum = new StatKeeper(true, "delayCum." + seed);
		notableStats.add(delayCum);
		// counts replicas for all units at a given time period
		StatKeeper replicaStat = new StatKeeper();
		// averages over time-> writes to file
		StatKeeper numReplicas = new StatKeeper(true, "numReplicas." + seed);
		notableStats.add(numReplicas);
		
		System.out.println("Setting up the simulation..");
		
		SimulatorConfig config = new SimulatorConfig(nServers);
		config.setRand(seed);
		config.setLogActivate(true);
		Simulator simulator = new Simulator( config);

		OutputGenerator output = new OutputGenerator(config.getContentTypes(), simulator.getNodes());

		System.out.println("Nodes have been created..");
		System.out.println(simulator.getVideoUnitDirectory().size()
				+ " Video Units in the system.");

		output.serverNodes2pajek("nodes.net");
		output.serverNodes2Gephi("nodes.gexf");

		System.out.println("Updating favorite videos for each user..");
		simulator.updateFavorites();

		System.out.println("Processing viewings of each user..");
		simulator.processViewings();

		System.out.println("Saving state to disk..");

		int div = simTime / nLosses;
		
		Vector<Integer> deletedServers = new Vector<Integer>();
		
		for (int i = 0; i < simTime; i++) {

			if (i == cLosses * div && cLosses <= nLosses) {
				int id = simulator.getRand().nextInt(nServers);
				while (deletedServers.contains(id)) {
					id = simulator.getRand().nextInt(nServers);
				}
				deletedServers.add(id);
				simulator.deleteServer(id);
				System.out.println("deleting server " + id);
				cLosses++;
			}

			if (i % 10 == 0) {

				timeStat.add(simulator.getSimTime());
				migrationMoves.add(simulator.migrationMoveCounter);
				migrationCopies.add(simulator.migrationCopyCounter);
				// count clients
				int clients = 0;
				for (ServerNode n : simulator.getNodes()) {
					clients += n.getConsumers().size();
				}
				seqRequestsDone.add(simulator.seqRequestFulfilledCounter);
				simulator.migrationMoveCounter = 0;
				simulator.migrationCopyCounter = 0;
				simulator.seqRequestFulfilledCounter = 0;

				avDelay.add(simulator.delayStat.mean());
				thruput.add(simulator.delayStat.n);

				ViewingCount vc = simulator.countViewings();
				viewingFulfilment.add(vc.getFulfilled());

//				double hr = 0.0;
//				int allRequests = simulator.hitStat.n;
//				if (allRequests != 0) {
//					hr = simulator.hitStat.countHit0 / (double) (allRequests);
//					hitrate.add(hr);
//				}
				delayCum.add(simulator.hitStat.mean());

				simulator.delayStat = new StatKeeper();
				StatKeeper diskUsageStat = simulator.serverDiskUsage();
				averageDiskUsage.add(diskUsageStat.mean());
//				worstcaseDiskUsage.add(diskUsageStat.max);

				for (int j = 0; j < simulator.getVideoUnitDirectory().size(); j++) {
					replicaStat.add(simulator.unitCount(simulator
							.getVideoUnitDirectory().get(j).getId()));
				}
				numReplicas.add(replicaStat.mean());
				replicaStat = new StatKeeper();
			}
			simulator.advance(0.1);
		}

		simulator.close();
		System.out.println("Simtime:" + simulator.simTimeRounded());
		System.out.println("done.");

	}

	static void evolve(int fitnessFunction) {
		Optimizer optimizer = new Optimizer();
		optimizer.evolve(50, fitnessFunction);
	}

	public static void main(String[] args) {

		// evolve(6);
		if (args.length > 0) {
			if (args[0].compareTo("evolve") == 0) {
				int fitnessFunction = 1;
				if (args.length > 1)
					fitnessFunction = Integer.parseInt(args[1]);
				evolve(fitnessFunction);
			}
			return;
		}

		notableStats.add(timeStat);
		int seed = 200;
		singleRun(seed);

		StatKeeper.saveNotableStats("n50_r_cl.csv", notableStats, true);
		return;
	}
}
